#include "cairo_font.h"
#if 0
cairo_font::cairo_font(IMLangFontLink2* fl, HFONT hFont, int size ) {
    init();
    m_font_link = fl;
    if(m_font_link) {
        m_font_link->AddRef();
    }
    m_size = size;
    set_font(hFont);
}

cairo_font::cairo_font(IMLangFontLink2* fl, LPCWSTR facename, int size, int weight, BOOL italic, BOOL strikeout, BOOL underline ) {
    init();
    m_size = size;
    m_font_link = fl;
    if(m_font_link) {
        m_font_link->AddRef();
    }

    LOGFONT lf;
    ZeroMemory(&lf, sizeof(lf));
    if(!lstrcmpi(facename, L"monospace")) {
        wcscpy_s(lf.lfFaceName, LF_FACESIZE, L"Courier New");
    } else if(!lstrcmpi(facename, L"serif")) {
        wcscpy_s(lf.lfFaceName, LF_FACESIZE, L"Times New Roman");
    } else if(!lstrcmpi(facename, L"sans-serif")) {
        wcscpy_s(lf.lfFaceName, LF_FACESIZE, L"Arial");
    } else if(!lstrcmpi(facename, L"fantasy")) {
        wcscpy_s(lf.lfFaceName, LF_FACESIZE, L"Impact");
    } else if(!lstrcmpi(facename, L"cursive")) {
        wcscpy_s(lf.lfFaceName, LF_FACESIZE, L"Comic Sans MS");
    } else {
        wcscpy_s(lf.lfFaceName, LF_FACESIZE, facename);
    }

    lf.lfHeight			= -size;
    lf.lfWeight			= weight;
    lf.lfItalic			= italic;
    lf.lfCharSet		= DEFAULT_CHARSET;
    lf.lfOutPrecision	= OUT_DEFAULT_PRECIS;
    lf.lfClipPrecision	= CLIP_DEFAULT_PRECIS;
    lf.lfQuality		= DEFAULT_QUALITY;
    lf.lfStrikeOut		= strikeout;
    lf.lfUnderline		= underline;

    HFONT fnt = CreateFontIndirect(&lf);
    set_font(fnt);
}

cairo_font::~cairo_font() {
    if(m_font_face) {
        cairo_font_face_destroy(m_font_face);
    }
    for(size_t i = 0; i < m_linked_fonts.size(); i++) {
        if(m_linked_fonts[i]->hFont) {
            m_font_link->ReleaseFont(m_linked_fonts[i]->hFont);
        }
        if(m_linked_fonts[i]->font_face) {
            cairo_font_face_destroy(m_linked_fonts[i]->font_face);
        }
    }
    m_linked_fonts.clear();
    if(m_font_link) {
        m_font_link->AddRef();
    }
    if(m_hFont) {
        DeleteObject(m_hFont);
    }
}
#endif

void cairo_font::show_text( cairo_t* cr, int x, int y, const litehtml::tchar_t* str ) {
    lock();
    text_chunk::vector chunks;
    split_text(str, chunks);
    cairo_set_font_size(cr, m_size);
    cairo_move_to(cr, x, y);
    for(size_t i = 0; i < chunks.size(); i++) {
        if(chunks[i]->font) {
            cairo_set_font_face(cr, chunks[i]->font->font_face);
        } else {
            cairo_set_font_face(cr, m_font_face);
        }
        cairo_show_text(cr, chunks[i]->text);
    }
    unlock();

    if(m_bUnderline) {
        int tw = text_width(cr, chunks);

        lock();
        cairo_set_line_width(cr, 1);
        cairo_move_to(cr, x, y + 1.5);
        cairo_line_to(cr, x + tw, y + 1.5);
        cairo_stroke(cr);
        unlock();
    }
    if(m_bStrikeOut) {
        int tw = text_width(cr, chunks);

        cairo_font_metrics fm;
        get_metrics(cr, &fm);

        int ln_y = y - fm.x_height / 2;

        lock();
        cairo_set_line_width(cr, 1);
        cairo_move_to(cr, x, (double) ln_y - 0.5);
        cairo_line_to(cr, x + tw, (double) ln_y - 0.5);
        cairo_stroke(cr);
        unlock();
    }

    free_text_chunks(chunks);
}

void cairo_font::split_text( const litehtml::tchar_t* src, text_chunk::vector& chunks ) {
    wchar_t* str;
#ifdef LITEHTML_UTF8
    str = cairo_font::utf8_to_wchar(src);
    wchar_t* str_start = str;
#else
    str = (wchar_t*) src;
#endif

    int cch = lstrlen(str);

    HDC hdc = GetDC(NULL);
    SelectObject(hdc, m_hFont);
    HRESULT hr = S_OK;
    while(cch > 0) {
        DWORD dwActualCodePages;
        long cchActual;
        if(m_font_link) {
            hr = m_font_link->GetStrCodePages(str, cch, m_font_code_pages, &dwActualCodePages, &cchActual);
        } else {
            hr = S_FALSE;
        }

        if(hr != S_OK) {
            break;
        }

        text_chunk* chk = new text_chunk;

        int sz = WideCharToMultiByte(CP_UTF8, 0, str, cchActual, chk->text, 0, NULL, NULL) + 1;
        chk->text = new CHAR[sz];
        sz = WideCharToMultiByte(CP_UTF8, 0, str, cchActual, chk->text, sz, NULL, NULL);
        chk->text[sz] = 0;
        chk->font = NULL;

        if(!(dwActualCodePages & m_font_code_pages)) {
            for(linked_font::vector::iterator i = m_linked_fonts.begin(); i != m_linked_fonts.end(); i++) {
                if((*i)->code_pages == dwActualCodePages) {
                    chk->font = (*i);
                    break;
                }
            }
            if(!chk->font) {
                linked_font* lkf = new linked_font;
                lkf->code_pages	= dwActualCodePages;
                lkf->hFont		= NULL;
                m_font_link->MapFont(hdc, dwActualCodePages, 0, &lkf->hFont);
                if (lkf->hFont) {
                    lkf->font_face = create_font_face(lkf->hFont);
                    m_linked_fonts.push_back(lkf);
                } else {
                    delete lkf;
                    lkf = NULL;
                }
                chk->font = lkf;
            }
        }

        chunks.push_back(chk);

        cch -= cchActual;
        str += cchActual;
    }

    if(hr != S_OK) {
        text_chunk* chk = new text_chunk;

        int sz = WideCharToMultiByte(CP_UTF8, 0, str, -1, chk->text, 0, NULL, NULL) + 1;
        chk->text = new CHAR[sz];
        sz = WideCharToMultiByte(CP_UTF8, 0, str, -1, chk->text, sz, NULL, NULL);
        chk->text[sz] = 0;
        chk->font = NULL;
        chunks.push_back(chk);
    }

    ReleaseDC(NULL, hdc);

#ifdef LITEHTML_UTF8
    delete str_start;
#endif
}

void cairo_font::free_text_chunks( text_chunk::vector& chunks ) {
    for(size_t i = 0; i < chunks.size(); i++) {
        delete chunks[i];
    }
    chunks.clear();
}

cairo_font_face_t* cairo_font::create_font_face( HFONT fnt ) {
    LOGFONT lf;
    GetObject(fnt, sizeof(LOGFONT), &lf);
    return cairo_win32_font_face_create_for_logfontw(&lf);
}

int cairo_font::text_width( cairo_t* cr, const litehtml::tchar_t* str ) {
    text_chunk::vector chunks;
    split_text(str, chunks);

    int ret = text_width(cr, chunks);

    free_text_chunks(chunks);

    return (int) ret;
}

int cairo_font::text_width( cairo_t* cr, text_chunk::vector& chunks ) {
    lock();
    cairo_set_font_size(cr, m_size);
    double ret = 0;
    for(size_t i = 0; i < chunks.size(); i++) {
        if(chunks[i]->font) {
            cairo_set_font_face(cr, chunks[i]->font->font_face);
        } else {
            cairo_set_font_face(cr, m_font_face);
        }
        cairo_text_extents_t ext;
        cairo_text_extents(cr, chunks[i]->text, &ext);
        ret += ext.x_advance;
    }
    unlock();

    return (int) ret;
}

void cairo_font::get_metrics(cairo_t* cr, cairo_font_metrics* fm ) {
    lock();
    cairo_set_font_face(cr, m_font_face);
    cairo_set_font_size(cr, m_size);
    cairo_font_extents_t ext;
    cairo_font_extents(cr, &ext);

    cairo_text_extents_t tex;
    cairo_text_extents(cr, "x", &tex);

    fm->ascent		= (int) ext.ascent;
    fm->descent		= (int) ext.descent;
    fm->height		= (int) (ext.ascent + ext.descent);
    fm->x_height	= (int) tex.height;
    unlock();
}

void cairo_font::set_font( HFONT hFont ) {
    clear();

    m_hFont				= hFont;
    m_font_face			= create_font_face(m_hFont);
    m_font_code_pages	= 0;
    if(m_font_link) {
        HDC hdc = GetDC(NULL);
        SelectObject(hdc, m_hFont);
        m_font_link->GetFontCodePages(hdc, m_hFont, &m_font_code_pages);
        ReleaseDC(NULL, hdc);
    }
    LOGFONT lf;
    GetObject(m_hFont, sizeof(LOGFONT), &lf);
    m_bUnderline	= lf.lfUnderline;
    m_bStrikeOut	= lf.lfStrikeOut;
}

void cairo_font::clear() {
    if(m_font_face) {
        cairo_font_face_destroy(m_font_face);
        m_font_face = NULL;
    }
    for(size_t i = 0; i < m_linked_fonts.size(); i++) {
        if(m_linked_fonts[i]->hFont && m_font_link) {
            m_font_link->ReleaseFont(m_linked_fonts[i]->hFont);
        }
        if(m_linked_fonts[i]->font_face) {
            cairo_font_face_destroy(m_linked_fonts[i]->font_face);
        }
    }
    m_linked_fonts.clear();
    if(m_hFont) {
        DeleteObject(m_hFont);
        m_hFont = NULL;
    }
}

void cairo_font::init() {
    m_hFont				= NULL;
    m_font_face			= NULL;
    m_font_link			= NULL;
    m_font_code_pages	= 0;
    m_size				= 0;
    m_bUnderline		= FALSE;
    m_bStrikeOut		= FALSE;
}

wchar_t* cairo_font::utf8_to_wchar( const char* src ) {
    if(!src) return NULL;

    int len = (int) strlen(src);
    wchar_t* ret = new wchar_t[len + 1];
    MultiByteToWideChar(CP_UTF8, 0, src, -1, ret, len + 1);
    return ret;
}

char* cairo_font::wchar_to_utf8( const wchar_t* src ) {
    if(!src) return NULL;

    int len = WideCharToMultiByte(CP_UTF8, 0, src, -1, NULL, 0, NULL, NULL);
    char* ret = new char[len];
    WideCharToMultiByte(CP_UTF8, 0, src, -1, ret, len, NULL, NULL);
    return ret;
}
